﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

namespace TinyAsset
{
    public class UnityHttpRequest : AsyncOperationBase<UnityHttpRequest>
    {
        public string Url { get; private set; } = string.Empty;

        public string Method { get; private set; } = UnityWebRequest.kHttpVerbGET;

        public int Timeout { get; private set; } = 0;

        public int MaxRetries { get; private set; } = 0;

        public int RetrySecondDelay { get; private set; } = 3;

        public float RetryElapse { get; private set; } = -1;

        public DownloadHandler downloadHandler { get; private set; } = null;

        public UploadHandler uploadHandler { get; private set; } = null;

        public UnityHttpResponse response { get; private set; } = null;

        public bool IsAborted { get; private set; } = false;

        public bool IsCompleted { get; private set; } = false;

        public int RetryCount { get; private set; } = 0;

        public float downloadProgress { get; private set; } = 0f;

        public float uploadProgress { get; private set; } = 0f;

        readonly Dictionary<string, string> headers = new Dictionary<string, string>();

        bool SendImmediately = false;

        UnityWebRequest webRequest;

        public UnityHttpRequest SetUrl(string url)
        {
            Url = url;
            return this;
        }

        public UnityHttpRequest SetMethod(string method)
        {
            Method = method;
            return this;
        }

        public UnityHttpRequest SetTimeout(int timeout)
        {
            Timeout = timeout;
            return this;
        }

        public UnityHttpRequest SetMaxRetries(int maxRetries)
        {
            MaxRetries = maxRetries;
            return this;
        }

        public UnityHttpRequest SetRetrySecondDelay(int retrySecondDelay)
        {
            RetrySecondDelay = retrySecondDelay;
            return this;
        }

        public UnityHttpRequest SetDownloadHandler(DownloadHandler handler)
        {
            downloadHandler = handler;
            return this;
        }

        public UnityHttpRequest SetUploadHandler(UploadHandler handler)
        {
            uploadHandler = handler;
            return this;
        }

        public UnityHttpRequest SetHeader(string key, string value)
        {
            headers[key] = value;
            return this;
        }

        public bool RemoveHeader(string key)
        {
            return headers.Remove(key);
        }

        public UnityHttpRequest CreateWebRequest()
        {
            webRequest = new UnityWebRequest(Url, Method, downloadHandler, uploadHandler);
            foreach (var header in headers)
                webRequest.SetRequestHeader(header.Key, header.Value);
            webRequest.timeout = Timeout;
            SendImmediately = true;
            return this;
        }

        public void Abort()
        {
            IsAborted = true;
            webRequest.Abort();
        }

        void SendWebRequest()
        {
            webRequest.SendWebRequest();
            SendImmediately = false;
        }

        void Clean()
        {
            if (webRequest != null)
            {
                webRequest.Dispose();
                webRequest = null;
            }
        }

        public void Update(float deltaTime, float unscaledDeltaTime)
        {
            if (RetryElapse > 0)
            {
                if (IsAborted)
                    RetryElapse = -1;
                else
                {
                    RetryElapse -= deltaTime;
                    if (RetryElapse > 0)
                        return;
                }
            }

            if (SendImmediately)
                SendWebRequest();

            downloadProgress = webRequest.downloadProgress;
            uploadProgress = webRequest.uploadProgress;

            if (webRequest.isDone)
            {
                IsCompleted = true;
                response = UnityHttpResponse.Create(webRequest);
                Clean();

                if (!response.isSuccessful && !IsAborted && RetryCount < MaxRetries)
                {
                    CreateWebRequest();

                    RetryCount++;
                    IsCompleted = false;
                    RetryElapse = RetrySecondDelay;
                }
            }
        }
    }
}